package com.niodata.dp.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4FastDecompressor;
import org.apache.commons.io.IOUtils;

public class Lz4Util {

  public static byte[] compressByte(byte[] data) {
    LZ4Factory factory = LZ4Factory.fastestInstance();
    LZ4Compressor compressor = factory.fastCompressor();
    byte[] compressedData = compressor.compress(data);
    byte[] lenByte = toByteArray(data.length);
    final byte[] joinedArray = new byte[lenByte.length + compressedData.length];
    System.arraycopy(lenByte, 0, joinedArray, 0, lenByte.length);
    System.arraycopy(compressedData, 0, joinedArray, lenByte.length, compressedData.length);
    return joinedArray;
  }

  public static byte[] decompressByte(byte[] compressedByte) {
    LZ4Factory factory = LZ4Factory.fastestInstance();
    byte[] lenBytes = new byte[4];
    System.arraycopy(compressedByte, 0, lenBytes, 0, 4);
    byte[] compressed = new byte[compressedByte.length - 4];
    System.arraycopy(compressedByte, 4, compressed, 0, compressedByte.length - 4);
    LZ4FastDecompressor decompressor = factory.fastDecompressor();
    int len = fromByteArray(lenBytes);
    return decompressor.decompress(compressed, len);
  }

  public static byte[] compressByStream(byte[] data) throws IOException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    try (LZ4BlockOutputStream out = new LZ4BlockOutputStream(outputStream)) {
      out.write(data);
    }
    return outputStream.toByteArray();
  }

  public static byte[] decompressByStream(byte[] data) throws IOException {
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
    try (LZ4BlockInputStream inputStream = new LZ4BlockInputStream(byteArrayInputStream)) {
      return IOUtils.toByteArray(inputStream);
    }
  }

  public static byte[] toByteArray(int value) {
    return ByteBuffer.allocate(4).putInt(value).array();
  }

  public static int fromByteArray(byte[] bytes) {
    return ByteBuffer.wrap(bytes).getInt();
  }
}
